<?php
/**
 * Defines the cube
 *
 * @package Cube
 * @since   Oct 19, 2006
 * @author  Christophe Thiriot
 */

jClasses::inc('cube~Center');
jClasses::inc('cube~Edge');
jClasses::inc('cube~Corner');

/**
 * The class of the cube
 *
 * @package Cube
*/
class Cube 
{
    const MOVE_U = 'U';
    const MOVE_D = 'D';
    const MOVE_R = 'R';
    const MOVE_L = 'L';
    const MOVE_F = 'F';
    const MOVE_B = 'B';
    const MOVE_M = 'M';
    const MOVE_E = 'E';
    const MOVE_S = 'S';
    const MOVE_SEPARATOR = ' ';

    const PIECE_TYPE_CENTER = 'center';
    const PIECE_TYPE_EDGE   = 'edge';
    const PIECE_TYPE_CORNER = 'corner';

    const COLOR_U = 'yellow';
    const COLOR_D = 'white';
    const COLOR_F = 'blue';
    const COLOR_B = 'green';
    const COLOR_R = 'red';
    const COLOR_L = 'orange';
    
    /**
     * @var array
     */
    private $tabCenters;

    /**
     * @var array
     */
    private $tabEdges;

    /**
     * @var array
     */
    private $tabCorners;

    /**
     * @var array
     */
    public static $tabColors = array(
        'centers' => array(
            1 => array(self::COLOR_U),
            2 => array(self::COLOR_F),
            3 => array(self::COLOR_R),
            4 => array(self::COLOR_B),
            5 => array(self::COLOR_L),
            6 => array(self::COLOR_D)
        ),
        'edges' => array(
            1  => array(self::COLOR_U, self::COLOR_F),
            2  => array(self::COLOR_U, self::COLOR_R),
            3  => array(self::COLOR_U, self::COLOR_B),
            4  => array(self::COLOR_U, self::COLOR_L),
            5  => array(self::COLOR_L, self::COLOR_F),
            6  => array(self::COLOR_R, self::COLOR_F),
            7  => array(self::COLOR_R, self::COLOR_B),
            8  => array(self::COLOR_L, self::COLOR_B),
            9  => array(self::COLOR_D, self::COLOR_F),
            10 => array(self::COLOR_D, self::COLOR_R),
            11 => array(self::COLOR_D, self::COLOR_B),
            12 => array(self::COLOR_D, self::COLOR_L)
        ),
        'corners' => array(
            1 => array(self::COLOR_U, self::COLOR_F, self::COLOR_L),
            2 => array(self::COLOR_U, self::COLOR_R, self::COLOR_F),
            3 => array(self::COLOR_U, self::COLOR_B, self::COLOR_R),
            4 => array(self::COLOR_U, self::COLOR_L, self::COLOR_B),
            5 => array(self::COLOR_D, self::COLOR_L, self::COLOR_F),
            6 => array(self::COLOR_D, self::COLOR_F, self::COLOR_R),
            7 => array(self::COLOR_D, self::COLOR_R, self::COLOR_B),
            8 => array(self::COLOR_D, self::COLOR_B, self::COLOR_L),
        )
    );

    /**
     * @var array
     *
     * The list of differents simple turns and their result
     * Example of edges for U :
     * 1 goes to 4, 4 goes to 3, 3 goes to 2 and 2 goes to 1
     * - is for ccw o flipped for edges
     * + is for cw
     */
    private $turns = array(
        self::MOVE_U => array(
            'edges'   => array( ' 1', ' 4', ' 3', ' 2'),
            'corners' => array( ' 1', ' 4', ' 3', ' 2')
        ),
        self::MOVE_D => array(
            'edges'   => array( ' 9',' 10',' 11',' 12'),
            'corners' => array( ' 5', ' 6', ' 7', ' 8')
        ),
        self::MOVE_R => array(
            'edges'   => array( '-2', '-7','-10', '-6'),
            'corners' => array( '+2', '-3', '+7', '-6')
        ),
        self::MOVE_L => array(
            'edges'   => array( '-4', '-5','-12', '-8'),
            'corners' => array( '-1', '+5', '-8', '+4')
        ),
        self::MOVE_F => array(
            'edges'   => array( ' 1', ' 6', ' 9', ' 5'),
            'corners' => array( '+1', '-2', '+6', '-5')
        ),
        self::MOVE_B => array(
            'edges'   => array( ' 3', ' 8',' 11', ' 7'),
            'corners' => array( '+3', '-4', '+8', '-7')
        ),
        self::MOVE_M => array(
            'centers' => array( ' 1', ' 2', ' 6', ' 4'),
            'edges'   => array( '-1', '-9','-11', '-3')
        ),
        self::MOVE_E => array(
            'centers' => array( ' 2', ' 3', ' 4', ' 5'),
            'edges'   => array( '-5', '-6', '-7', '-8')
        ),
        self::MOVE_S => array(
            'centers' => array( ' 1',  ' 3', ' 6', ' 5'),
            'edges'   => array( '-2', '-10','-12', '-4')
        )
    );

    /**
     * Constructor
     */
    public function __construct()
    {
        foreach (range(1, Center::MAX_CENTER) as $index) 
        {
            $this->tabCenters[$index] = new Center($index);
        }
        foreach (range(1, Edge::MAX_EDGE) as $index) 
        {
            $this->tabEdges[$index] = new Edge($index);
        }
        foreach (range(1, Corner::MAX_CORNER) as $index) 
        {
            $this->tabCorners[$index] = new Corner($index);
        }
    }

    /**
     * just doing base turns, no combination
     * handling orientation, too
     * @param string $turn One letter of a turn
     */
    public function turn($mov) 
    {      
        if (array_key_exists($mov, $this->turns)) 
        {
	        // extracting the positions and orientations
	        $tabPositions    = $this->_extractPositions($this->turns[$mov]);
	        $tabOrientations = $this->_extractOrientation($this->turns[$mov]);

            // orientation handling
            if (true === array_key_exists('edges', $tabOrientations))
            {
                foreach ($tabOrientations['edges'] as $key => $value) 
                {
                    if ($value === '-') 
                    {
                        $this->tabEdges[$tabPositions['edges'][$key]]->toggleOrientation();
                    }
                }
            }
            if (true === array_key_exists('corners', $tabOrientations))
            {
                foreach ($tabOrientations['corners'] as $key=>$value) 
                {
                    if ($value === '-') 
                    {
    					$this->tabCorners[$tabPositions['corners'][$key]]->turnCCW();
                    } else if ($value === '+') 
                    {
    					$this->tabCorners[$tabPositions['corners'][$key]]->turnCW();
    				}
    			}
            }

			// positions handling
            $this->_changePosition('centers', $this->tabCenters, $tabPositions);
            $this->_changePosition('edges', $this->tabEdges, $tabPositions);
            $this->_changePosition('corners', $this->tabCorners, $tabPositions);
        } else 
        {
            throw new Exception('Cube::turn : unknown move : ' . $mov);
        }
    }

	/**
	 * @return array positions of a permutation
	 */
    private function _extractPositions($tabturn) 
    {
    	$tabresult = array();
        foreach ($tabturn as $cubietype => $tab) 
        {
            foreach ($tab as $value)
            {
				$tabresult[$cubietype][] = (int) substr($value, 1);
			}
    	}
    	return $tabresult;
    }

	/**
	 * @return array orientations of a permutation
	 */
    private function _extractOrientation($tabturn) 
    {
    	$tabresult = array();
        foreach ($tabturn as $cubietype => $tab) 
        {
            foreach ($tab as $value) 
            {
				$tabresult[$cubietype][] = $value[0];
			}
        }

    	return $tabresult;
    }

    /**
     * Serialization of the cube
     * @return string
     */
    public function __toString() 
    {
        $mystring = '(';
        foreach ($this->tabCenters as $cubie) 
        {
            $mystring.= $cubie->getOriginPosition() . ' ';
        }
        $mystring = chop($mystring);
        $mystring.= ') ';

        $mystring.= '(';
        foreach ($this->tabEdges as $cubie) 
        {
            $mystring.= $cubie->getOriginPosition() . $cubie->orientToString() . ' ';
        }
        $mystring = chop($mystring);
        $mystring.= ') (';
        foreach ($this->tabCorners as $cubie) 
        {
            $mystring.= $cubie->getOriginPosition() . $cubie->orientToString() . ' ';
        }
        $mystring = chop($mystring);
        $mystring.= ")";

        return $mystring;
    }

    /**
     * execute an suite of movements discribed by algos of simple movements (no ' or 2 or anything)
     * @param string $strturn
     */
    public function alg($strturn)
    {
        $tturn = explode(' ', $strturn);
        foreach ($tturn as $key => $value)
        {
            $this->turn($value);
        }
    }

    /**
     * get the data of the cube in the form of a map
     */
    public function getState()
    {
        $return = array();

        foreach ($this->tabCenters as $position => $center)
        {
            $return[] = array(
                'type'        => self::PIECE_TYPE_CENTER,
                'origin'      => $center->getOriginPosition(),
                'position'    => $position,
                'orientation' => 0
            );
        }

        foreach ($this->tabEdges as $position => $edge)
        {
            $return[] = array(
                'type'        => self::PIECE_TYPE_EDGE,
                'origin'      => $edge->getOriginPosition(),
                'position'    => $position,
                'orientation' => $edge->getOrientation()
            );
        }

        foreach ($this->tabCorners as $position => $corner)
        {
            $return[] = array(
                'type'        => self::PIECE_TYPE_CORNER,
                'origin'      => $corner->getOriginPosition(),
                'position'    => $position,
                'orientation' => $corner->getOrientation()
            );
        }

        return $return;
    }

    public function getPiece($piecetype, $pieceposition)
    {
        $piece = null;
        if ($piecetype === 'center')
        {
            $piece = $this->tabCenters[$pieceposition];
        } elseif ($piecetype === 'edge')
        {
            $piece = $this->tabEdges[$pieceposition];
        } elseif ($piecetype === 'corner')
        {
            $piece = $this->tabCorners[$pieceposition];
        }

        $return = array();
        $return['origin']      = $piece->getOriginPosition();
        $return['orientation'] = $piece->getOrientation();

        return $return;
    }

    public function getColor($piecetype, $pieceposition, $pieceorientation)
    {

    }

    private function _changePosition($strPiece, &$tabPiece, $tabPositions)
    {
        if (true === array_key_exists($strPiece, $tabPositions))
        {
            $temp = $tabPiece[$tabPositions[$strPiece][3]];
            foreach (range(3, 1) as $i)
            {
                $tabPiece[$tabPositions[$strPiece][$i]] = $tabPiece[$tabPositions[$strPiece][$i - 1]];
            }
            $tabPiece[$tabPositions[$strPiece][0]] = $temp;
        }
    }
}
?>
